package com.hengfoxwolf.bannerdemo
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.get
import kotlinx.coroutines.*
import java.lang.Math.abs

class BannerContainer @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null
) : ViewGroup(context, attributeSet), CoroutineScope by MainScope() {

    private val marginValue = context.resources.getDimension(R.dimen.dp_10).toInt()
    private val defaultHeight = context.resources.getDimension(R.dimen.dp_180).toInt()

    private val circleRadius = context.resources.getDimension(R.dimen.dp_3)
    private val circleItemSpace = context.resources.getDimension(R.dimen.dp_10)

    private val bannerItemMap by lazy {
        mutableMapOf<Int, String>().apply {
            put(R.drawable.layout_bg_01, "one")
            put(R.drawable.layout_bg_02, "two")
            put(R.drawable.layout_bg_03, "three")
            put(R.drawable.layout_bg_04, "four")
            put(R.drawable.layout_bg_05, "five")
        }
    }

    private val unSelectedPaint by lazy {
        Paint().apply {
            isAntiAlias = true
            isDither = true
            color = context.resources.getColor(R.color.color_e91e63)
            style = Paint.Style.FILL
            strokeWidth = context.resources.getDimension(R.dimen.dp_5)
        }
    }

    private val selectedPaint by lazy {
        Paint().apply {
            isAntiAlias = true
            isDither = true
            color = context.resources.getColor(R.color.color_2196f3)
            style = Paint.Style.FILL
            strokeWidth = context.resources.getDimension(R.dimen.dp_5)
        }
    }

    private var flagX = 0
    private var flagY = 0
    private var downX = 0
    private var downY = 0

    private var movedUnit = 0
        set(value) {
            field = value
        }

    private var totalMovedX = 0
    private var maxMovedValue = 0

    private var currentIndex = 0

    private var showIndicator = true

    private var mainJob: Job? = null
    private var childJob: Job? = null

    private var objAnimator: ObjectAnimator? = null

    override fun onFinishInflate() {
        super.onFinishInflate()
        for ((key, value) in bannerItemMap) {
            addBannerItem(key, value)
        }

        mainJob = launch {
            delay(2000)
            childJob = createJob()
            childJob?.start()
        }
        mainJob?.start()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        childJob?.cancel()
        mainJob?.cancel()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        for (i in 0.until(childCount)) {
            val child = get(i)
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0)
            val layoutParams = child.layoutParams as MarginLayoutParams
            layoutParams.marginStart = marginValue
            layoutParams.topMargin = marginValue
            layoutParams.marginEnd = marginValue
            layoutParams.bottomMargin = marginValue
            child.layoutParams = layoutParams
        }

        setMeasuredDimension(
            MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd,
            MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom
        )
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        var totalLeftRight = 0
        for (i in 0.until(childCount)) {
            val child = get(i)
            val layoutParams = child.layoutParams as MarginLayoutParams
            val childLeft = layoutParams.marginStart
            val childTop = layoutParams.topMargin
            val childRight = childLeft + child.measuredWidth
            val childBottom = childTop + child.measuredHeight
            child.layout(
                childLeft + totalLeftRight,
                childTop,
                childRight + totalLeftRight,
                childBottom
            )
            maxMovedValue = totalLeftRight
            totalLeftRight += childRight + layoutParams.marginEnd
        }
    }

    override fun dispatchDraw(canvas: Canvas?) {
        super.dispatchDraw(canvas)
        canvas?.apply {
            if (showIndicator) {
                val baseCx = measuredWidth.toFloat() / 10
                var itemSpace = 0F
                val cy = defaultHeight.toFloat() * 9 / 10
                for (i in 0.until(childCount)) {
                    drawCircle(
                        baseCx + itemSpace - totalMovedX,
                        cy,
                        circleRadius,
                        if (i == currentIndex) selectedPaint else unSelectedPaint
                    )
                    itemSpace += circleItemSpace
                }
            }
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        when (ev?.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(TAG, "onInterceptTouchEvent---ACTION_DOWN")
                downX = ev.x.toInt()
                downY = ev.y.toInt()
                flagX = downX
                flagY = downY
                childJob?.cancel()
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(TAG, "onInterceptTouchEvent---ACTION_MOVE")
                val moveX = ev.x.toInt()
                val moveY = ev.y.toInt()
                val diffX = moveX - downX
                val diffY = moveY - downY
                val handled =
                    abs(diffX) > abs(diffY) && abs(diffX) >= MIN_SLIDE_DISTANCE
                return if (handled) {
                    downX = moveX
                    downY = moveY
                    true
                } else {
                    downX = moveX
                    downY = moveY
                    super.onInterceptTouchEvent(ev)
                }
            }
        }
        return super.onInterceptTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        performClick()
        when (event?.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(TAG, "onTouchEvent---ACTION_DOWN")
                downX = event.x.toInt()
                downY = event.y.toInt()
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(TAG, "onTouchEvent---ACTION_MOVE")
                val moveX = event.x.toInt()
                val moveY = event.y.toInt()
                val diffX = moveX - downX
                val diffY = moveY - downY
                if (abs(diffX) > abs(diffY)) {
                    totalMovedX += diffX
                    totalMovedX = if (diffX > 0) {
                        Math.min(totalMovedX, 0)
                    } else {
                        Math.max(totalMovedX, -maxMovedValue)
                    }
                    scrollTo(-totalMovedX, 0)
                }
                downX = moveX
                downY = moveY
            }
            MotionEvent.ACTION_UP -> {
                Log.d(TAG, "onTouchEvent---ACTION_UP")
                val viceMovedX = totalMovedX % measuredWidth
                var mainMovedX = totalMovedX - viceMovedX
                val condition = abs(viceMovedX) > measuredWidth * 3 / 11
                val upX = event.x.toInt()
                val upY = event.y.toInt()
                val diffX = upX - flagX
                val diffY = upY - flagY
                if (abs(diffX) > abs(diffY)) {
                    mainMovedX += if (diffX > 0) {
                        Log.d(TAG, "diffX > 0,condition:${condition}")
                        if (condition) 0 else (if (mainMovedX == 0) 0 else -measuredWidth)
                    } else {
                        Log.d(TAG, "diffX < 0,condition:${condition}")
                        if (condition) -measuredWidth else 0
                    }
                    totalMovedX = if (diffX > 0) {
                        if (condition) {
                            currentIndex = (currentIndex - 1) % childCount
                            currentIndex = Math.max(currentIndex, 0)
                        }
                        Math.min(mainMovedX, 0)
                    } else {
                        if (condition) {
                            currentIndex = (currentIndex + 1) % childCount
                            currentIndex = Math.min(currentIndex, childCount - 1)
                        }
                        Math.max(mainMovedX, -maxMovedValue)
                    }
                    scrollTo(-totalMovedX, 0)
                    if (condition) {
                        invalidate()
                    }
                }

                childJob = createJob()
                childJob?.start()
            }
        }
        return true
    }

    override fun performClick(): Boolean {
        return super.performClick()
    }

    override fun generateLayoutParams(attrs: AttributeSet?): MarginLayoutParams {
        return MarginLayoutParams(context, attrs)
    }

    private fun createJob(): Job {
        return launch {
            while (true) {
                delay(3000)
                objAnimator = ObjectAnimator.ofInt(this@BannerContainer, "movedUnit", 0, 1)
                objAnimator?.apply {
                    duration = 200
                    addUpdateListener {
                        val value = (measuredWidth * it.animatedFraction).toInt()
                        totalMovedX -= value
                        Log.d(TAG, "value:${value}")
                        scrollTo(-totalMovedX, 0)
                        if (value == measuredWidth) {
                            currentIndex++
                            currentIndex %= childCount
                            Log.d(TAG, "totalMovedX:${totalMovedX}")
                            invalidate()
                            showIndicator = true
                        } else {
                            totalMovedX += value
                        }
                    }
                    interpolator = AccelerateDecelerateInterpolator()
                }
                objAnimator?.start()

                if (totalMovedX == -maxMovedValue) {
                    totalMovedX = measuredWidth
                    showIndicator = false
                }
            }
        }
    }

    private fun addBannerItem(resId: Int, desc: String) {
        val layout = LayoutInflater.from(context).inflate(R.layout.banner_item_layout, this, false)
        val bannerView = layout.findViewById<AppCompatImageView>(R.id.iv_banner_item)
        bannerView.setBackgroundResource(resId)
        bannerView.setOnClickListener {
            context.showToast(desc)
        }
        addView(
            layout,
            MarginLayoutParams(MarginLayoutParams.MATCH_PARENT, defaultHeight)
        )
    }

    companion object {
        const val TAG = "BannerContainer"
        const val MIN_SLIDE_DISTANCE = 8
    }

}